#include "qe_log.h"
#include "qe_time.h"
#include "qe_memory.h"
#include "qe_assert.h"
#include "qe_driver.h"
#include "libfdt.h"



QELOG_DOMAIN("ps7-xdcfg");

#define PS7_SLCR_BASE                   0xF8000000
#define PS7_XDCFG_BASE                  0xF8007000

#define SLCR_UNLOCK_KEY 			    0x0000DF0D
#define SLCR_PSS_RST_MASK 			    0x00000001

#define XDCFG_UNLOCK_KEY 		        0x757BDF0D



typedef struct 
{
    volatile qe_u32 scl;                /* Secure Configuration Lock */
    volatile qe_u32 slcr_lock;          /* SLCR Write Protection Lock */
    volatile qe_u32 slcr_unlock;        /* SLCR Write Protection Unlock */
    volatile qe_u32 slcr_lock_sta;      /* SLCR Write Protection Status */
    volatile qe_u32 reserved[124];
    volatile qe_u32 pss_rst_ctrl;       /* PS Software Reset Control */
} ps7_slcr_reg;

typedef struct 
{
    volatile qe_u32 cr;                 /* Control Register */
    volatile qe_u32 lock;               /* Locks for the Control Register */
    volatile qe_u32 cfg;                /* Configuration Register */
    volatile qe_u32 int_sts;            /* Interrupt Status */
    volatile qe_u32 int_mask;           /* Interrupt Mask */
    volatile qe_u32 sr;                 /* Miscellaneous Status */
    volatile qe_u32 dma_src;            /* DMA Source Address */
    volatile qe_u32 dma_dst;            /* DMA Destination Address */
    volatile qe_u32 dma_src_len;        /* DMA Source Transfer Length */
    volatile qe_u32 dma_dst_len;        /* DMA Destination Transfer Length */
    volatile qe_u32 multiboot_addr;     /* Multi-Boot Address Pointer */
    volatile qe_u32 unlock;             /* Unlock Control */
    volatile qe_u32 mctrl;              /* Miscellaneous Control */
    volatile qe_u32 if_cfg;             /* XADC Interface Configuration */
    volatile qe_u32 if_int_sts;         /* XADC Interface Interrupt Status */
    volatile qe_u32 if_int_mask;        /* XADC Interface Interrupt Mask */
    volatile qe_u32 if_msts;            /* XADC Interface Miscellaneous Status */
    volatile qe_u32 if_cmdfifo;         /* XADC Interface Command FIFO Data Port */
    volatile qe_u32 if_rdfifo;          /* XADC Interface Data FIFO Data Port */
    volatile qe_u32 if_mctl;            /* XADC Interface Miscellaneous Control */
} ps7_xdcfg_reg;

typedef struct
{
    ps7_xdcfg_reg *reg;
    ps7_slcr_reg *slcr_reg;
    qe_hwboot_dev hwboot;
} ps7_xdcfg_dev;



static void ps7_xdcfg_boot(qe_hwboot_dev *dev, qe_ubase addr)
{
    ps7_xdcfg_dev *cfg = (ps7_xdcfg_dev *)qe_hwboot_dev_priv(dev);

    qe_notice("boot %x", addr);

    qe_u32 boot_addr = addr / 0x8000;

    /* Set multiboot address */
    cfg->reg->unlock = XDCFG_UNLOCK_KEY;
    cfg->reg->multiboot_addr = boot_addr;

    /* Reset */
    cfg->slcr_reg->slcr_unlock = SLCR_UNLOCK_KEY;
    cfg->slcr_reg->pss_rst_ctrl = SLCR_PSS_RST_MASK;
}

static qe_hwboot_ops ps7_xdcfg_ops = {
    .boot = ps7_xdcfg_boot,
};

static qe_ret ps7_xdcfg_probe(const void *fdt, int offset)
{
    int len;
    const char *name;
    ps7_xdcfg_dev *dev;

    name = fdt_get_name(fdt, offset, QE_NULL);

    dev = qe_malloc(sizeof(ps7_xdcfg_dev));
    qe_assert(dev);

    dev->reg = (ps7_xdcfg_reg *)PS7_XDCFG_BASE;
    dev->slcr_reg = (ps7_slcr_reg *)PS7_SLCR_BASE;

    qe_hwboot_register(&dev->hwboot, name, &ps7_xdcfg_ops, dev);

    qe_debug("register %s", name);

    return qe_ok;
}

static const qe_device_id ps7_xdcfg_ids[] = {
    {.compatible = "xlnx,ps7-xdc-1.00.a",},
    {}
};

QE_DRIVER(ps7_xdcfg) = {
    .name = "ps7-xdcfg",
    .of_match = ps7_xdcfg_ids,
    .probe = ps7_xdcfg_probe,
};

QE_DRIVER_FORCE_EXPORT(ps7_xdcfg);